/*
 * Copyright (c) 2009 The University of Edinburgh
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Lei Zhang
 */

#ifndef __ARCH_UNICORE_INSTS_INTEGER_HH__
#define __ARCH_UNICORE_INSTS_INTEGER_HH__

#include "arch/unicore/insts/static_inst.hh"
#include "base/bitfield.hh"
#include "base/cprintf.hh"

namespace UnicoreISA
{

/**
 * We provide a base class for integer operations and then inherit for
 * several other classes. These specialise for instructions using immediate
 * values and also rotate instructions. We also need to have versions that
 * consider the Rc and OE bits.
 */

/**
 * Base class for integer operations.
 */
class IntOp : public UnicoreStaticInst
{
  protected:

    /// Constructor
    IntOp(const char *mnem, MachInst _machInst, OpClass __opClass)
      : UnicoreStaticInst(mnem, _machInst, __opClass)
    {
       bc = machInst.bc_mv; // inherited from UnicoreStaticInst
    }

    inline uint8_t
    makeAFRField(uint64_t result) const
    {
        uint8_t ze, sn;
        ze = (result == 0);
        sn = (result >> 63);

        uint8_t afr = (ze << 2) | (sn << 3);
        return afr;
    }

    /* Compute the AFR field using unsigned comparison */
	inline uint8_t 
    makeAFRFieldSub(uint64_t a, uint64_t b) const
    {
		uint8_t ov, ca, ze, sn;
		uint64_t tmp = a + ~b + 1;
		
        // significant bit
		uint8_t sb_a = a >> 63,
				sb_b = b >> 63,
				sb_t = tmp >> 63;
		ov = ((sb_a == 1) && (sb_b == 0) && (sb_t == 0)) ||
			 ((sb_a == 0) && (sb_b == 1) && (sb_t == 1));
		ca = (a >= b); // FIXME
		ze = (tmp == 0);
		sn = (tmp >> 63);

		uint8_t afr = ov | (ca << 1) | (ze << 2) | (sn << 3);
		return afr;
	}
   
    inline uint8_t 
    makeAFRFieldSubc(uint64_t a, uint64_t b, uint64_t cin) const
    {
		uint8_t ov, ca, ze, sn;
		uint64_t tmp = a + ~b + cin;
		
        // significant bit
		uint8_t sb_a = a >> 63,
				sb_b = b >> 63,
				sb_t = tmp >> 63;
		ov = ((sb_a == 1) && (sb_b == 0) && (sb_t == 0)) ||
			 ((sb_a == 0) && (sb_b == 1) && (sb_t == 1));
		ca = (a >= (b + cin)); // FIXME
		ze = (tmp == 0);
		sn = (tmp >> 63);

		uint8_t afr = ov | (ca << 1) | (ze << 2) | (sn << 3);
		return afr;
	}
   
    inline uint8_t 
    makeAFRFieldRsub(uint64_t a, uint64_t b) const
    {
		uint8_t ov, ca, ze, sn;
		uint64_t tmp = ~a + b + 1;
		
        // significant bit
		uint8_t sb_a = a >> 63,
				sb_b = b >> 63,
				sb_t = tmp >> 63;
		ov = ((sb_a == 1) && (sb_b == 0) && (sb_t == 1)) ||
			 ((sb_a == 0) && (sb_b == 1) && (sb_t == 0));
		ca = (b >= a); // FIXME
		ze = (tmp == 0);
		sn = (tmp >> 63);

		uint8_t afr = ov | (ca << 1) | (ze << 2) | (sn << 3);
		return afr;
	}
 
    inline uint8_t 
    makeAFRFieldRsubc(uint64_t a, uint64_t b, uint64_t cin) const
    {
		uint8_t ov, ca, ze, sn;
		uint64_t tmp = ~a + b + cin;
		
        // significant bit
		uint8_t sb_a = a >> 63,
				sb_b = b >> 63,
				sb_t = tmp >> 63;
		ov = ((sb_a == 1) && (sb_b == 0) && (sb_t == 1)) ||
			 ((sb_a == 0) && (sb_b == 1) && (sb_t == 0));
		ca = (b >= (a + cin)); // FIXME
		ze = (tmp == 0);
		sn = (tmp >> 63);

		uint8_t afr = ov | (ca << 1) | (ze << 2) | (sn << 3);
		return afr;
	}

    inline uint8_t
    makeAFRFieldAdd(uint64_t a, uint64_t b) const
    {
        uint8_t ov, ca, ze, sn;
        uint64_t tmp = a + b;

        // significant bit
        uint8_t sb_a = a >> 63,
                sb_b = b >> 63,
                sb_t = tmp >> 63;
        ov = ((sb_a == 1) && (sb_b == 1) && (sb_t == 0)) ||
             ((sb_a == 0) && (sb_b == 0) && (sb_t == 1));
        ca = (tmp < a); // FIXME
        ze = (tmp == 0);
        sn = (tmp >> 63);

        uint8_t afr = ov | (ca << 1) | (ze << 2) | (sn << 3);
        return afr;
    }
 
    inline uint8_t
    makeAFRFieldAddc(uint64_t a, uint64_t b, uint64_t cin) const
    {
        uint8_t ov, ca, ze, sn;
        uint64_t tmp = a + b + cin;

        // significant bit
        uint8_t sb_a = a >> 63,
                sb_b = b >> 63,
                sb_t = tmp >> 63;
        ov = ((sb_a == 1) && (sb_b == 1) && (sb_t == 0)) ||
             ((sb_a == 0) && (sb_b == 0) && (sb_t == 1));
        ca = (tmp < a) || ((tmp == a) && cin); // FIXME
        ze = (tmp == 0);
        sn = (tmp >> 63);

        uint8_t afr = ov | (ca << 1) | (ze << 2) | (sn << 3);
        return afr;
    }


    inline uint8_t
    makeAFRField(uint32_t result) const
    {
        // note: the type conversion is necessary
        uint64_t xresult = (uint64_t)result << 32;
        return makeAFRField(xresult);
    }
   
    inline uint8_t
    makeAFRFieldSub(uint32_t a, uint32_t b) const
    {
        // shift left by 32 bits
        uint64_t xa = (uint64_t)a << 32;
        uint64_t xb = (uint64_t)b << 32;
        return makeAFRFieldSub(xa, xb);
    }
     
    inline uint8_t
    makeAFRFieldSubc(uint32_t a, uint32_t b, uint64_t cin) const
    {
        // shift left by 32 bits
        uint64_t xa = (uint64_t)a << 32;
        uint64_t xb = (uint64_t)b << 32;
        uint64_t xcin = (uint64_t)cin << 32;
        return makeAFRFieldSubc(xa, xb, xcin);
    }
    
    inline uint8_t
    makeAFRFieldRsub(uint32_t a, uint32_t b) const
    {
        // shift left by 32 bits
        uint64_t xa = (uint64_t)a << 32;
        uint64_t xb = (uint64_t)b << 32;
        return makeAFRFieldRsub(xa, xb);
    }
     
    inline uint8_t
    makeAFRFieldRsubc(uint32_t a, uint32_t b, uint64_t cin) const
    {
        // shift left by 32 bits
        uint64_t xa = (uint64_t)a << 32;
        uint64_t xb = (uint64_t)b << 32;
        uint64_t xcin = (uint64_t)cin << 32;
        return makeAFRFieldRsubc(xa, xb, xcin);
    }
    
    inline uint8_t
    makeAFRFieldAdd(uint32_t a, uint32_t b) const
    {
        // shift left by 32 bits
        uint64_t xa = (uint64_t)a << 32;
        uint64_t xb = (uint64_t)b << 32;
        return makeAFRFieldAdd(xa, xb);
    }
     
    inline uint8_t
    makeAFRFieldAddc(uint32_t a, uint32_t b, uint64_t cin) const
    {
        // shift left by 32 bits
        uint64_t xa = (uint64_t)a << 32;
        uint64_t xb = (uint64_t)b << 32;
        uint64_t xcin = (uint64_t)cin << 32;
        return makeAFRFieldAddc(xa, xb, xcin);
    }
   
    std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const;
};


/**
 * Class for integer immediate (signed and unsigned) operations.
 */
class IntImmOp : public IntOp
{
  protected:

    int64_t imm;
    uint64_t uimm;

    /// Constructor
    IntImmOp(const char *mnem, MachInst _machInst, OpClass __opClass)
      : IntOp(mnem, _machInst, __opClass),
        imm(sext<11>(machInst.si)), // sign-extend the 11-bit immediate
        uimm(machInst.si)
    {
    }
   
    std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const;
};


} // namespace UnicoreISA

#endif //__ARCH_UNICORE_INSTS_INTEGER_HH__
